package service

import (
	"Short_chain_cats/database"
	. "Short_chain_cats/user/model"
	"Short_chain_cats/util"
	"context"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"time"

	"github.com/xuri/excelize/v2"

	"github.com/gin-gonic/gin"

	"github.com/go-redis/redis/v8"
)

type UserService struct{}

func NewUserService() *UserService {
	return &UserService{}
}

func (r UserService) Create(ctx context.Context, param *CreateUserCommand) (*User, error) {
	//校验邮箱验证码
	if !util.VerifyEmailCode(ctx, param.UserEmail, param.EmailCode) {
		return &User{}, NewErr(EmailCodeErr)
	}
	// 1.账户不能重复,邮箱不能重复
	count, err := UserModel.GetCountByAccount(ctx, param.UserAccount)
	if count > 0 {
		return &User{}, NewErr(AccountRepeat)
	}
	count, err = UserModel.GetCountByEmail(ctx, param.UserEmail)
	if count > 0 {
		return &User{}, NewErr(EmailRepeat)
	}
	// 2. 加密
	param.UserPassword, err = util.HashPassword(param.UserPassword)
	if err != nil {
		return &User{}, NewErr(BcryptPassword)
	}
	//3. 插入数据
	user, err := UserModel.Create(ctx, param)
	if err != nil {
		return &User{}, NewErr(RegisterFailed)
	}
	return user, nil
}

func (r UserService) Login(ctx context.Context, param *UserLoginCommand) (*User, error) {
	// 1.用户不存在
	user, _ := UserModel.GetUserByAccount(ctx, param.UserAccount)
	if user.Id == 0 {
		return &User{}, NewErr(AccountNotExist)
	}
	// 2. 匹配密码
	if !util.CheckPasswordHash(param.UserPassword, user.UserPassword) {
		return &User{}, NewErr(PasswordErr)
	}
	// 3. 记录用户的登录态
	token, err := util.GenerateToken(user.Id, user.UserRole)
	if err != nil {
		return &User{}, NewErr(TokenCreateFailed)
	}
	user.AToken = token
	return user, nil
}

func (r UserService) SendEmailCode(ctx context.Context, param *EmailCommand) error {
	//随机生成6位数字的验证码
	emailCode := util.GenerateEmailCode()
	msg := fmt.Sprintf("你的邮箱验证码是: %s", emailCode)
	//发送生成的验证码
	if err := util.SendEmail(param.UserEmail, msg); err != nil {
		return NewErr(SendEmailErr)
	}
	//缓存验证码
	if err := util.StoreEmailCode(ctx, param.UserEmail, emailCode); err != nil {
		return NewErr(StoreEmailErr)
	}
	return nil
}

func (r UserService) UpdateUserInfo(ctx *gin.Context, param *UpdateUserCommand) (*User, error) {
	userIDAny, _ := ctx.Get("userID")
	// 尝试将 userID 断言为 int 类型
	userID, ok := userIDAny.(int)
	if !ok {
		// 如果断言失败，返回错误
		return &User{}, NewErr(UserTokenFailed)
	}
	user, err := UserModel.GetById(ctx, userID)
	if err != nil {
		return &User{}, err
	}
	if user.UserAccount != param.UserAccount {
		// 1.账户不能重复
		count, _ := UserModel.GetCountByAccount(ctx, param.UserAccount)
		if count > 0 {
			return &User{}, NewErr(AccountRepeat)
		}
	}

	//user.Id = userID
	//user.UserProfile = param.UserProfile
	//user.UserAccount = param.UserAccount
	//user.UpdateTime = time.Now()

	us := map[string]interface{}{
		"userProfile": param.UserProfile,
		"userAccount": param.UserAccount,
		"updateTime":  time.Now(),
	}

	// 使用事务来确保数据一致性
	tx := database.NewOrm(ctx).Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			panic(r)
		}
	}()
	err = UserModel.UpdateUser(ctx, userID, us)
	if err != nil {
		tx.Rollback()
		return &User{}, NewErr(UpdateUserFailed)
	}
	// 提交事务
	if err = tx.Commit().Error; err != nil {
		return &User{}, NewErr(UpdateUserErr)
	}
	u, err := UserModel.GetById(ctx, userID)
	if err != nil {
		return &User{}, err
	}
	return u, nil
}

func (r UserService) ForgetPassword(ctx *gin.Context, param *ForgetPasswordCommand) (*User, error) {
	//校验邮箱验证码
	if !util.VerifyEmailCode(ctx, param.UserEmail, param.EmailCode) {
		return &User{}, NewErr(EmailCodeErr)
	}
	user, _ := UserModel.GetByEmail(ctx, param.UserEmail)
	user.UserPassword, _ = util.HashPassword(param.NewPassword)
	u, err := UserModel.UpdateUserInfo(ctx, user)
	if err != nil {
		return &User{}, NewErr(UpdateUserPasswordFailed)
	}
	return u, nil
}

func (r UserService) UpdateUserPassword(ctx *gin.Context, param *UpdateUserPasswordCommand) (*User, error) {
	userIDAny, _ := ctx.Get("userID")
	// 尝试将 userID 断言为 int 类型
	userID, ok := userIDAny.(int)
	if !ok {
		// 如果断言失败，返回错误
		return &User{}, NewErr(UserTokenFailed)
	}
	user, _ := UserModel.GetById(ctx, userID)
	// 原密码是否正确
	if !util.CheckPasswordHash(param.OldPassword, user.UserPassword) {
		return &User{}, NewErr(PasswordErr)
	}
	user.UserPassword, _ = util.HashPassword(param.NewPassword)
	u, err := UserModel.UpdateUserInfo(ctx, user)
	if err != nil {
		return &User{}, NewErr(UpdateUserPasswordFailed)
	}
	return u, nil
}

func (r UserService) GetRegisteredUserCount(ctx context.Context) (int64, error) {
	//先去redis里面查,查不到去mysql查
	key := "user:count"
	count, err := database.RedisClient.Get(ctx, key).Int64()
	//count, err := database.RedisClient.Get(ctx, "registered_user_count").Int64()
	if err != nil {
		if err == redis.Nil {
			// 如果 Redis 中没有找到键，返回 0
			return 0, NewErr(RedisKeyErr)
		}
		//查不到去mysql查
		return UserModel.GetUserCount(ctx)
	}
	return count, nil
}

func (r UserService) ShortChainCount(ctx context.Context) (int64, error) {
	return UserModel.GetShortChainCount(ctx)
}

func (r UserService) RefreshToken(ctx context.Context, userAccount string) (string, error) {
	user, err := UserModel.GetUserByAccount(ctx, userAccount)
	if err != nil {
		return "", err
	}
	return util.RefreshToken(user.Id, user.UserRole)
}

func (r UserService) GetDailyRegisterCounts(ctx context.Context, startDate, endDate string) ([]DailyCount, error) {
	// 获取 Redis 中存储的每天注册人数
	counts, err := database.RedisClient.HGetAll(ctx, "user:register:daily").Result()
	if err != nil {
		return nil, NewErr(GetRedisDataFailed)
	}
	// 初始化结果集和缺失日期集
	var result []DailyCount
	var missingDates []string
	// 查找在 Redis 中缺失的日期
	for d := startDate; d <= endDate; d = GetNextDate(d) {
		if _, exists := counts[d]; !exists {
			missingDates = append(missingDates, d)
		}
	}
	// 如果有缺失的日期，从数据库查询并合并结果
	if len(missingDates) > 0 {
		dbResults, err := UserModel.GetDailyRegisterCountsToDB(ctx, missingDates)
		if err != nil {
			return nil, err
		}
		// 确保 Date 字段是 YYYY-MM-DD 格式
		for i := range dbResults {
			dbResults[i].Date = dbResults[i].Date[:10] // 只取前10个字符 YYYY-MM-DD
		}
		//result = append(result, dbResults...)
		// 将数据库结果加入到result中
		for _, date := range missingDates {
			found := false
			for _, dbResult := range dbResults {
				if dbResult.Date == date {
					result = append(result, dbResult)
					found = true
					break
				}
			}
			if !found {
				result = append(result, DailyCount{Date: date, Count: 0})
			}
		}
	}

	for date, count := range counts {
		if date >= startDate && date <= endDate {
			countInt, _ := strconv.ParseInt(count, 10, 64)
			result = append(result, DailyCount{Date: date, Count: countInt})
		}
	}
	// 按日期排序结果集(正序)
	sort.Slice(result, func(i, j int) bool {
		return result[i].Date < result[j].Date
	})
	return result, nil
}

func (r UserService) GetDailyShortChainCounts(ctx context.Context, startDate, endDate string) ([]DailyCount, error) {
	// 获取 Redis 中存储的每天短链生成数量
	counts, err := database.RedisClient.HGetAll(ctx, "user:shortChain:daily").Result()
	if err != nil {
		return nil, NewErr(GetRedisDataFailed)
	}

	// 初始化结果集和缺失日期集
	var result []DailyCount
	var missingDates []string

	// 查找在 Redis 中缺失的日期
	for d := startDate; d <= endDate; d = GetNextDate(d) {
		if _, exists := counts[d]; !exists {
			missingDates = append(missingDates, d)
		}
	}
	// 如果有缺失的日期，从数据库查询并合并结果
	if len(missingDates) > 0 {
		dbResults, err := UserModel.GetDailyShortChainCountsToDB(ctx, missingDates)
		if err != nil {
			return nil, err
		}
		// 确保 Date 字段是 YYYY-MM-DD 格式
		for i := range dbResults {
			dbResults[i].Date = dbResults[i].Date[:10] // 只取前10个字符 YYYY-MM-DD
		}
		//result = append(result, dbResults...)
		// 将数据库结果加入到result中
		for _, date := range missingDates {
			found := false
			for _, dbResult := range dbResults {
				if dbResult.Date == date {
					result = append(result, dbResult)
					found = true
					break
				}
			}
			if !found {
				result = append(result, DailyCount{Date: date, Count: 0})
			}
		}
	}

	for date, count := range counts {
		if date >= startDate && date <= endDate {
			countInt, _ := strconv.ParseInt(count, 10, 64)
			result = append(result, DailyCount{Date: date, Count: countInt})
		}
	}
	// 按日期排序结果集(正序)
	sort.Slice(result, func(i, j int) bool {
		return result[i].Date < result[j].Date
	})
	return result, nil
}

func (r UserService) UploadPicture(ctx *gin.Context, url string) error {
	userIDAny, _ := ctx.Get("userID")
	// 尝试将 userID 断言为 int 类型
	userID, ok := userIDAny.(int)
	if !ok {
		// 如果断言失败，返回错误
		return NewErr(UserTokenFailed)
	}
	if err := UserModel.UploadAvatarById(ctx, userID, url); err != nil {
		return NewErr(UploadAvatarFailed)
	}
	return nil
}

func (r UserService) GetAll(ctx context.Context, userAccount, userProfile, userRole string, page, pageSize, createOrder, updateOrder int) ([]*User, int64, error) {
	user, totalCount, err := UserModel.GetAll(ctx, userAccount, userProfile, userRole, page, pageSize, createOrder, updateOrder)
	if err != nil {
		return nil, 0, err
	}
	return user, totalCount, nil
}

// GetNextDate 辅助函数：返回下一天的日期字符串
func GetNextDate(dateStr string) string {
	date, _ := time.Parse("2006-01-02", dateStr)
	nextDate := date.AddDate(0, 0, 1)
	return nextDate.Format("2006-01-02")
}

// SaveDailyRegisterCountsToDB 定期将 Redis 数据持久化到数据库
func SaveDailyRegisterCountsToDB() {
	ctx := context.Background()
	counts, err := database.RedisClient.HGetAll(ctx, "user:register:daily").Result()
	if err != nil {
		log.Printf("Failed to get daily register counts from Redis: %v", err)
		return
	}
	for date, count := range counts {
		countInt, _ := strconv.ParseInt(count, 10, 64)
		dailyRegisterCount := DailyCount{
			Date:  date,
			Count: countInt,
		}
		if UserModel.SaveDailyRegisterCountsToDB(ctx, dailyRegisterCount); err != nil {
			log.Printf("Failed to save daily register count to database: %v", err)
			return
		}
	}
	// 不清空 Redis 中的 Map，只设置过期时间
	//database.RedisClient.Expire(ctx, "user:register:daily", 7*24*time.Hour)
}

// SaveDailyShortChainCountsToDB 定期将 Redis 数据持久化到数据库
func SaveDailyShortChainCountsToDB() {
	ctx := context.Background()
	counts, err := database.RedisClient.HGetAll(ctx, "user:shortChain:daily").Result()
	if err != nil {
		log.Printf("Failed to get daily shortChain counts from Redis: %v", err)
		return
	}
	for date, count := range counts {
		countInt, _ := strconv.ParseInt(count, 10, 64)
		dailyShortChainCount := DailyCount{
			Date:  date,
			Count: countInt,
		}
		if UserModel.SaveDailyShortChainCountsToDB(ctx, dailyShortChainCount); err != nil {
			log.Printf("Failed to save daily shortChain count to database: %v", err)
			return
		}
	}
	// 不清空 Redis 中的 Map，只设置过期时间
	//database.RedisClient.Expire(ctx, "user:register:daily", 7*24*time.Hour)
}

func StartScheduler() {
	ticker := time.NewTicker(24 * time.Hour)
	defer ticker.Stop()
	for range ticker.C {
		SaveDailyRegisterCountsToDB()
		SaveDailyShortChainCountsToDB()
	}
}

func (r UserService) EditUserInfo(ctx *gin.Context, param *EditUserCommand) (*User, error) {
	user, err := UserModel.GetById(ctx, param.Id)
	if err != nil {
		return &User{}, err
	}
	if user.UserAccount != param.UserAccount {
		// 1.账户不能重复
		count, _ := UserModel.GetCountByAccount(ctx, param.UserAccount)
		if count > 0 {
			return &User{}, NewErr(AccountRepeat)
		}
	}
	//user.Id = param.Id
	//user.UserProfile = param.UserProfile
	//user.UserAccount = param.UserAccount
	//user.UserRole = param.UserRole
	//user.UpdateTime = time.Now()
	us := map[string]interface{}{
		"userProfile": param.UserProfile,
		"userAccount": param.UserAccount,
		"userRole":    param.UserRole,
		"updateTime":  time.Now(),
	}
	// 使用事务来确保数据一致性
	tx := database.NewOrm(ctx).Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			panic(r)
		}
	}()
	err = UserModel.UpdateUser(ctx, param.Id, us)
	if err != nil {
		tx.Rollback()
		return &User{}, NewErr(UpdateUserFailed)
	}
	// 提交事务
	if err = tx.Commit().Error; err != nil {
		return &User{}, NewErr(UpdateUserErr)
	}
	u, err := UserModel.GetById(ctx, param.Id)
	if err != nil {
		return &User{}, err
	}
	return u, nil
}

func (r UserService) DeleteUsers(ctx context.Context, id []int) error {
	if err := UserModel.DeleteUsers(ctx, id); err != nil {
		return err
	}
	return nil
}

func (r UserService) ExportExcel(users []*User) (string, error) {
	// 创建新的 Excel 文件
	file := excelize.NewFile()

	// 添加一个工作表
	sheetName := "Sheet1"
	file.SetSheetName("Sheet1", sheetName)

	// 写入表头
	file.SetCellValue(sheetName, "A1", "id")
	file.SetCellValue(sheetName, "B1", "用户名")
	file.SetCellValue(sheetName, "C1", "头像")
	file.SetCellValue(sheetName, "D1", "个人简介")
	file.SetCellValue(sheetName, "E1", "权限")
	file.SetCellValue(sheetName, "F1", "创建时间")
	file.SetCellValue(sheetName, "G1", "更新时间")
	// 写入用户数据
	row := 2
	for _, user := range users {
		file.SetCellValue(sheetName, fmt.Sprintf("A%d", row), user.Id)
		file.SetCellValue(sheetName, fmt.Sprintf("B%d", row), user.UserAccount)
		file.SetCellValue(sheetName, fmt.Sprintf("C%d", row), user.UserAvatar)
		file.SetCellValue(sheetName, fmt.Sprintf("D%d", row), user.UserProfile)
		file.SetCellValue(sheetName, fmt.Sprintf("E%d", row), user.UserRole)
		file.SetCellValue(sheetName, fmt.Sprintf("F%d", row), user.CreateTime.Format("2006-01-02 15:04:05"))
		file.SetCellValue(sheetName, fmt.Sprintf("G%d", row), user.UpdateTime.Format("2006-01-02 15:04:05"))
		row++
	}

	// 保存 Excel 文件到临时目录
	tmpDir := os.TempDir()
	filePath := filepath.Join(tmpDir, "users.xlsx")
	if err := file.SaveAs(filePath); err != nil {
		return "", err
	}
	// 返回导出的文件路径
	return filePath, nil
}

func GetEmailById(ctx context.Context, id int, msg string) error {
	email, err := UserModel.GetEmailById(ctx, id)
	if err != nil || email == "" {
		return NewErr(GetEmailErr)
	}
	if err = util.SendEmail(email, msg); err != nil {
		return NewErr(SendShortChainErr)
	}
	return nil
}
